package com.atguigu.tingshu.common.thread;

import com.atguigu.tingshu.common.zipkinConfig.ZipkinHelper;
import com.atguigu.tingshu.common.zipkinConfig.ZipkinTaskDecorator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.*;

/**
 * 自定义线程池
 *
 */
@Configuration
public class ThreadPoolConfig {



    /**
     * JDK自带线程池（JUC包提供的）
     * 核心线程=最大线程数 一般应用两类  N 代表CPU核数  初步得出数值，后续采用压测调整合适数量
     *   CPU密集型：N/N+1
     *   （Java应用程序一般）IO密集型：2N/2N+1
     * 阻塞队列长度：200-500之间
     * @return
     */
    /**
     * 自定义线程池对象
     * @return
     */
    @Bean
    public ThreadPoolExecutor threadPoolExecutor(){
        //1.动态得到核心线程数 IO密集型：CPU逻辑处理器个数*2
        int processorsCount = Runtime.getRuntime().availableProcessors();
        int coreCount = processorsCount * 2;

        //2.通过线程池构造创建线程池对象
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                coreCount,
                coreCount,
                0,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(200),
                Executors.defaultThreadFactory(),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {//r:被拒绝任务  executor:线程池对象
                        //自定义拒绝策略：重试-将任务再次提交给线程执行
                        try {
                            Thread.sleep(200);//等0.2秒
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        executor.submit(r); //提交
                    }
                }
        );
        //3.线程池核心线程是在第一个任务提交时才会创建，在初始化这里用下面的方法提前创建好一个核心线程，让第一个任务来的时候可以有线程直接用
        threadPoolExecutor.prestartCoreThread();

        return threadPoolExecutor;
    }



    @Autowired
    private ZipkinHelper zipkinHelper;

    /**
     * zipkin 3的版本会默认使用spring线程池，而每个微服务都有自己的线程池，这样各个链路都是分开的，会看不到服务之间的依赖关系
     * 所以使用这里自定义线程池替换spring微服务自己内部的线程池
     * 项目中选择spring线程池
     * Spring提供线程池threadPoolTaskExecutor
     *
     * @return
     */
    @Bean
    public Executor threadPoolTaskExecutor() {
        int count = Runtime.getRuntime().availableProcessors();
        int threadCount = count*2+1;
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // 核心池大小
        taskExecutor.setCorePoolSize(threadCount);
        // 最大线程数
        taskExecutor.setMaxPoolSize(threadCount);
        // 队列程度
        taskExecutor.setQueueCapacity(300);
        // 线程空闲时间
        taskExecutor.setKeepAliveSeconds(0);
        // 线程前缀名称
        taskExecutor.setThreadNamePrefix("sync-tingshu-Executor--");
        // 该方法用来设置 线程池关闭 的时候 等待 所有任务都完成后，再继续 销毁 其他的 Bean，
        // 这样这些 异步任务 的 销毁 就会先于 数据库连接池对象 的销毁。
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        // 任务的等待时间 如果超过这个时间还没有销毁就 强制销毁，以确保应用最后能够被关闭，而不是阻塞住。
        taskExecutor.setAwaitTerminationSeconds(300);
        //设置解决zipkin链路追踪不完整装饰器对象
        taskExecutor.setTaskDecorator(new ZipkinTaskDecorator(zipkinHelper));
        // 线程不够用时由调用的线程处理该任务
        taskExecutor.setRejectedExecutionHandler((task, executor)->{
            //将拒绝任务再次提交给线程池执行
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            executor.submit(task);
        });
        return taskExecutor;
    }

}
